package primitives;

import javax.vecmath.Matrix4d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector4d;

import shaders.MatteShader;
import shaders.Shader;

import essential.Axis;
import essential.Ray;
import essential.RayCastResult;

public class Plane extends Primitive {
	private Vector3d N;
	private double D;
	protected Matrix4d transform = new Matrix4d();
	private Matrix4d tmpMat = new Matrix4d();
	private Vector4d tmpVec = new Vector4d();

	public Renderable copy() {
		Plane t = new Plane(N, D);
		t.transform = new Matrix4d(transform);
		t.setShader(getShader().copy());
		return t;
	}
	
	public Plane(){
		this(new Vector3d(),1);
	}
	
	public Plane(Vector3d N, double D){
		this.N = new Vector3d(N);
		this.N.normalize();
		this.D = D;
		this.transform.setIdentity();
		this.setShader(new MatteShader());
		this.aabb = new AxisAlignedBoundingBox(new Vector3d(Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE),
				new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE)
				);
	}
	
	public Vector3d getNormal(Vector3d v) {
		return this.N;
	}

	public RayCastResult intersect(Ray ray) {
		double d = this.N.dot(ray.getDirection());

//		System.out.println("Normal = " + N);
//		System.out.println("Dist = " + D);
//		System.out.println("Ray = " + ray.toString());

		if (d != 0)
		{
			double dist = -(this.N.dot(ray.getOrigin()) + this.D)/d;
			//float dist = -(DOT( m_Plane.N, a_Ray.GetOrigin() ) + m_Plane.D) / d;
			if (dist > 0)
			{
				if (dist < ray.getImpactDist() ) 
				{
					ray.impactColor = this.getShader().getColor();
					ray.setImpactDist(dist);
					ray.setLastRenderable(this);
					ray.impactNormal = N;
					return RayCastResult.HIT;
				}
			}
		}
		return RayCastResult.MISS;
	}

	public void setD(double D) {
		this.D = D;
	}

	public double getD() {
		return D;
	}

	public void setNormal(Vector3d n) {
		N = n;
	}

	public Vector3d getNormal() {
		return N;
	}
	
	public double distanceTo(Vector3d v) {
		return -(N.dot(v) + D) / N.lengthSquared();
	}

//	@Override
	public Vector3d getOrigin() {
		Vector3d orig = new Vector3d(N);
		orig.scale(D);
		return orig;
	}
	
	public String toString() {
		return "Plane(" + N + ", " + D + ")";
	}


	@Override
	public void rotate(Axis axis, double angles) {
		tmpMat.setIdentity();

		switch(axis) {
		case X:
			tmpMat.rotX(angles);
			break;
		case Y:
			tmpMat.rotY(angles);
			break;
		case Z:
			tmpMat.rotZ(angles);
			break;
		}

 		transform.mul(tmpMat, transform);
	}

	@Override
	public void scale(Vector3d pos) {}

	@Override
	public void translate(Vector3d pos) {
		tmpMat.setIdentity();
		tmpMat.setTranslation(pos);
		transform.mul(tmpMat, transform);
	}

	@Override
	public void apply(Transform t) {
		transform.mul(t.matrix, transform);
	}

	@Override
	public void set(Transform t) {
		transform.setIdentity();
		transform.mul(t.matrix, transform);
	}

	Vector3d tmpVecN = new Vector3d();
	
	@Override
	public void freezeTransform() {
		/*N.normalize();
		N.scale(D);
		tmpVec.set(tmpVecN);
		tmpVec.w = 1.0;
		transform.transform(tmpVec);
		N.set(tmpVec.x, tmpVec.y, tmpVec.z);
		D = N.length();*/
		N.scale(D);
		tmpVec.set(N);
		tmpVec.w = 1.0;
		transform.transform(tmpVec);
		N.set(tmpVec.x, tmpVec.y, tmpVec.z);
		D = N.length();
		N.normalize();
	}

	@Override
	public void recalcAABB() {}

	
}
